package com.bitsemart;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;

/**
 * Created by jorge on 24/02/15.
 * <p/>
 * Uso de la clase:
 * - En la carpeta app/src/main/assets debe estar el fichero properties.oracle con los datos del
 * propietario de la Base de Datos.
 * - Insertar este código en la clase MainActivity.java:
 * **************************************************************************************************
 * try {
 * InputStream in = getAssets().open("properties.oracle");
 * BufferedReader reader = new BufferedReader(new InputStreamReader(in));
 * BDHendrix bdHendrix = new BDHendrix(reader.readLine(),reader.readLine());
 * }
 * catch (IOException e) {}
 * **************************************************************************************************
 * - Y pasar el objeto bdHendrix a las Activitys para poder utilizar la Base de Datos.
 */
public class BDHendrix extends AsyncTask<String, Object, ArrayList<Videojuego>> {

    private String db_driver;
    private String db_username;
    private String db_password;
    private Context context;

    public BDHendrix(String db_username, String db_password, Context context) {
        this.context = context;
        this.db_username = db_username;
        this.db_password = db_password;
        db_driver = "jdbc:oracle:thin:@hendrix-oracle.cps.unizar.es:1521:vicious";

        if (db_driver.contains("oracle")) {
            try {
                Class.forName("oracle.jdbc.OracleDriver");
            } catch (ClassNotFoundException e) {
                System.err.println("Oracle driver not found");
                e.printStackTrace();
            }
        }
    }

    /**
     * Método para insertar videojuego nuevo en la Base de Datos.
     *
     * @param titulo
     * @param year
     * @param precio
     * @param genero
     * @param desarrollador
     * @param descripcion
     * @param rutaImagen
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public void insertarJuego(String titulo, int year, int precio, String genero, String desarrollador,
                              String descripcion, String rutaImagen) throws InterruptedException, ExecutionException {
        // El VJID se inserta automáticamente mediante un Trigger
        new BDHendrix(db_username, db_password, context).execute("insertarJuego", titulo,
                Integer.toString(year), Integer.toString(precio), genero,
                desarrollador, descripcion, rutaImagen);
    }
    /**
     * Método para eliminar videojuego de la Base de Datos.
     *
     * @param id ID del Videojuego a eliminar
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public void eliminarJuego(int id) throws InterruptedException, ExecutionException {
        new BDHendrix(db_username, db_password, context).execute("eliminarJuego", Integer.toString(id));
    }

    /**
     * Método para obtener un videojuego de la Base de Datos.
     *
     * @param id ID del Videojuego a obtener
     * @return objeto Videojuego con todos los datos del mismo.
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public Videojuego obtenerJuego(int id) throws InterruptedException, ExecutionException {
        return new BDHendrix(db_username, db_password, context).execute("obtenerJuego", Integer.toString(id)).get().get(0);
    }

    /**
     * Método para obtener todos los videojuegos de la Base de Datos.
     *
     * @return ArrayList de objetos Videojuego con todos los datos de cada uno.
     * @throws InterruptedException
     * @throws ExecutionException   Ha habido algún problema con la conexión a internet
     */
    public ArrayList<Videojuego> obtenerTodosLosJuegos() throws InterruptedException, ExecutionException {
        return new BDHendrix(db_username, db_password, context).execute("obtenerTodosLosJuegos").get();
    }

    /**
     * Método para modificar los datos de un videojuego de la Base de Datos.
     *
     * @param id                 ID del juego a modificar Nuevos datos:
     * @param nuevoTitulo
     * @param nuevoYear
     * @param nuevoPrecio
     * @param nuevoGenero
     * @param nuevoDesarrollador
     * @param nuevaDescripcion
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public void modificarInfo(int id, String nuevoTitulo, int nuevoYear,
                              int nuevoPrecio, String nuevoGenero, String nuevoDesarrollador,
                              String nuevaDescripcion) throws InterruptedException, ExecutionException {
        new BDHendrix(db_username, db_password, context).execute("modificarInfo", Integer.toString(id), nuevoTitulo,
                Integer.toString(nuevoYear), Integer.toString(nuevoPrecio),
                nuevoGenero, nuevoDesarrollador, nuevaDescripcion);
    }

    /**
     * Método para modificar la imagen de un videojuego de la Base de Datos
     *
     * @param id    ID del juego a modificar
     * @param rutaNuevaImagen   ruta de la nueva imagen
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public void modificarImagen(int id, String rutaNuevaImagen)
            throws InterruptedException, ExecutionException {
        new BDHendrix(db_username, db_password, context).execute("modificarImagen",
                Integer.toString(id), rutaNuevaImagen);
    }

    /**
     * Método para actualizar el número de descargas de un videojuego.
     *
     * @param juego objeto Videojuego con los datos del videojuego descargado
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public void actualizarDescargas(Videojuego juego) throws InterruptedException, ExecutionException {
        new BDHendrix(db_username, db_password, context).execute("actualizarDescargas", Integer.toString(juego.getId()),
                Integer.toString(juego.getNumDescargas() + 1));
    }

    /**
     * Método que devuelve los videojuegos cuyo id aparece en el array ids.
     * (Su uso principal es para obtener los videojuegos del carrito de la compra)
     *
     * @param ids Array con los ids de videojuegos a obtener
     * @return ArrayList de videojuegos
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public ArrayList<Videojuego> obtenerVideojuegos(ArrayList<Integer> ids) throws InterruptedException, ExecutionException {
        if (ids.isEmpty()) {
            return new ArrayList<Videojuego>();
        } else {
            int i = 0;
            String s = "vjid=" + Integer.toString(ids.get(0));
            for (i = 1; i < ids.size(); i++) {
                s = s + " OR vjid=" + Integer.toString(ids.get(i));
            }
            return new BDHendrix(db_username, db_password, context).execute("obtenerVideojuegos", s).get();
        }
    }

    /**
     * Método que devuelve los videojuegos cuya desarrolladora es el campo desarrolladora introducido
     * y su genero el campo genero.
     * Los campos con valor vacío o null se ignoran
     *
     * @param desarrolladora
     * @param genero
     * @return  ArrayList de Videojuegos que pasan el filtro
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public ArrayList<Videojuego> filtrar(String desarrolladora, String genero) throws InterruptedException, ExecutionException {
        String query = "";
        if (desarrolladora != null && !desarrolladora.equals("")) {
            query += " WHERE upper(DESARROLLADOR) = upper('"+desarrolladora+"')";
            if (genero != null && !genero.equals("")) {
                query += " AND upper(GENERO) = upper('"+genero+"')";
            }
        }
        else {
            if (genero != null && !genero.equals("")) {
                query += " WHERE upper(GENERO) = upper('"+genero+"')";
            }
        }
        return new BDHendrix(db_username, db_password, context).execute("filtrarYbuscar",query).get();
    }

    /**
     * Método que busca en la Base de datos videojuegos con el cuyo titulo se asemeja al valor introducido
     * en el campo de búsqueda.
     *
     * @param titulo
     * @return  ArrayList de Videojuegos que cumplen la búsqueda
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public ArrayList<Videojuego> buscar(String titulo, String orderBy) throws InterruptedException, ExecutionException {
        String query = "";
        if (titulo!=null && !titulo.equals("")) {
            Scanner scanner = new Scanner(titulo);
            query = " WHERE upper(titulo) like upper('%" + scanner.next() + "%')";
            while (scanner.hasNext()) {
                query += " OR upper(titulo) like upper('%" + scanner.next() + "%')";
            }
            switch(orderBy.toUpperCase()) {
                case "TITULO":
                    query += " ORDER BY upper(TITULO) ASC";
                    break;
                case "YEAR":
                    query += " ORDER BY YEAR DESC";
                    break;
                case "PRECIO":
                    query += " ORDER BY PRECIO ASC";
                    break;
                case "NUMDESCARGAS":
                    query += " ORDER BY NUMDESCARGAS DESC";
                    break;
            }
            scanner.close();
        }
        return new BDHendrix(db_username, db_password, context).execute("filtrarYbuscar",query).get();
    }

    /**
     * Método que devuelve todos los videojuegos ordenados por Título
     *
     * @return ArrayList de Videojuegos ordenado por título
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public ArrayList<Videojuego> ordenarPorTitulo() throws InterruptedException, ExecutionException {
        String query = "upper(TITULO) ASC";
        return new BDHendrix(db_username, db_password, context).execute("ordenar",query).get();
    }

    /**
     * Método que devuelve todos los videojuegos ordenados por Precio
     *
     * @return ArrayList de Videojuegos ordenados por precio
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public ArrayList<Videojuego> ordenarPorPrecio() throws InterruptedException, ExecutionException {
        String query = "PRECIO ASC";
        return new BDHendrix(db_username, db_password, context).execute("ordenar",query).get();
    }

    /**
     * Método que devuelve todos los videojuegos ordenados por el número de descargas
     *
     * @return ArrayList de Videojuegos ordenados por el número de descargas
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public ArrayList<Videojuego> ordenarPorNumDescargas() throws InterruptedException, ExecutionException {
        String query = "NUMDESCARGAS DESC";
        return new BDHendrix(db_username, db_password, context).execute("ordenar",query).get();
    }

    /**
     * Método que devuelve todos los videojuegos ordenados por el año de lanzamiento
     *
     * @return ArrayList de Videojuegos ordenados por el año de lanzamiento
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public ArrayList<Videojuego> ordenarPorYear() throws InterruptedException, ExecutionException {
        String query = "YEAR DESC";
        return new BDHendrix(db_username, db_password, context).execute("ordenar",query).get();
    }

    /**
     * Método que ejecuta las consultas a la Base de Datos en Segundo Plano.
     */
    @Override
    protected ArrayList<Videojuego> doInBackground(String... params) {
        try {
            Connection connection = DriverManager.getConnection(db_driver, db_username,
                    db_password);
            PreparedStatement pstmt = null;
            ArrayList<Videojuego> result = new ArrayList<Videojuego>();
            ResultSet rs = null;
            FileInputStream fs = null;
            Blob blob = null;
            byte byteArray[] = null;
            Bitmap imagen = null;

            switch (params[0]) {
                case "insertarJuego":
                    try {
                        fs = new FileInputStream(new File(params[7]));
                        pstmt = connection.prepareStatement("INSERT INTO VIDEOJUEGO VALUES (?,?,?,?,?,?,?,?,?)");
                        pstmt.setInt(1, 0);
                        pstmt.setString(2, params[1]);
                        pstmt.setString(3, params[2]);
                        pstmt.setString(4, params[3]);
                        pstmt.setString(5, params[4]);
                        pstmt.setString(6, params[5]);
                        pstmt.setString(7, params[6]);
                        pstmt.setInt(8, 0);
                        pstmt.setBinaryStream(9, fs, fs.available());
                        pstmt.executeUpdate();
                        fs.close();
                    } catch (Exception e) {
                        InputStream in = context.getAssets().open("imageNotAvailable.jpg");
                        pstmt = connection.prepareStatement("INSERT INTO VIDEOJUEGO VALUES (?,?,?,?,?,?,?,?,?)");
                        pstmt.setInt(1, 0);
                        pstmt.setString(2, params[1]);
                        pstmt.setString(3, params[2]);
                        pstmt.setString(4, params[3]);
                        pstmt.setString(5, params[4]);
                        pstmt.setString(6, params[5]);
                        pstmt.setString(7, params[6]);
                        pstmt.setInt(8, 0);
                        pstmt.setBinaryStream(9, in, in.available());
                        pstmt.executeUpdate();
                    }
                    break;
                case "obtenerJuego":
                    pstmt = connection.prepareStatement("SELECT * FROM VIDEOJUEGO WHERE VJID=?");
                    pstmt.setString(1, params[1]);
                    rs = pstmt.executeQuery();
                    if (rs.next()) {
                        blob = rs.getBlob(9);
                        byteArray = blob.getBytes(1, (int) blob.length());
                        imagen = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
                        result.add(new Videojuego(rs.getInt(1), rs.getString(2), rs.getInt(3),
                                rs.getInt(4), rs.getString(5), rs.getString(6), rs.getString(7),
                                rs.getInt(8), imagen));
                    }
                    break;
                case "obtenerTodosLosJuegos":
                    pstmt = connection.prepareStatement("SELECT * FROM VIDEOJUEGO");
                    rs = pstmt.executeQuery();
                    blob = null;
                    byteArray = null;
                    imagen = null;
                    while (rs.next()) {
                        blob = rs.getBlob(9);
                        byteArray = blob.getBytes(1, (int) blob.length());
                        imagen = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
                        result.add(new Videojuego(rs.getInt(1), rs.getString(2), rs
                                .getInt(3), rs.getInt(4), rs.getString(5), rs.getString(6),
                                rs.getString(7), rs.getInt(8), imagen));
                    }
                    break;
                case "eliminarJuego":
                    pstmt = connection.prepareStatement("DELETE FROM VIDEOJUEGO WHERE VJID=?");
                    pstmt.setString(1, params[1]);
                    pstmt.executeUpdate();
                    break;
                case "modificarInfo":
                    pstmt = connection.prepareStatement("UPDATE VIDEOJUEGO SET TITULO=?,YEAR=?," +
                            "PRECIO=?,GENERO=?,DESARROLLADOR=?,DESCRIPCION=? WHERE VJID=?");
                    pstmt.setString(1, params[2]);
                    pstmt.setString(2, params[3]);
                    pstmt.setString(3, params[4]);
                    pstmt.setString(4, params[5]);
                    pstmt.setString(5, params[6]);
                    pstmt.setString(6, params[7]);
                    pstmt.setString(7, params[1]);
                    pstmt.executeUpdate();
                    break;
                case "actualizarDescargas":
                    pstmt = connection.prepareStatement("UPDATE VIDEOJUEGO SET NUMDESCARGAS=? WHERE VJID=?");
                    pstmt.setString(1, params[2]);
                    pstmt.setString(2, params[1]);
                    pstmt.executeUpdate();
                    break;
                case "obtenerVideojuegos":
                    pstmt = connection.prepareStatement("SELECT * FROM VIDEOJUEGO WHERE " + params[1]);
                    rs = pstmt.executeQuery();
                    blob = null;
                    byteArray = null;
                    imagen = null;
                    while (rs.next()) {
                        blob = rs.getBlob(9);
                        byteArray = blob.getBytes(1, (int) blob.length());
                        imagen = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
                        result.add(new Videojuego(rs.getInt(1), rs.getString(2), rs
                                .getInt(3), rs.getInt(4), rs.getString(5), rs.getString(6),
                                rs.getString(7), rs.getInt(8), imagen));
                    }
                    break;
                case "modificarImagen":
                    try {
                        fs = new FileInputStream(new File(params[2]));
                        pstmt = connection.prepareStatement("UPDATE VIDEOJUEGO SET IMAGEN=? WHERE VJID=?");
                        pstmt.setBinaryStream(1, fs, fs.available());
                        pstmt.setString(2, params[1]);
                        pstmt.executeUpdate();
                        fs.close();
                    } catch (Exception e) {}
                    break;
                case "filtrarYbuscar":
                    pstmt = connection.prepareStatement("SELECT * FROM VIDEOJUEGO"+params[1]);
                    rs = pstmt.executeQuery();
                    blob = null;
                    byteArray = null;
                    imagen = null;
                    while (rs.next()) {
                        blob = rs.getBlob(9);
                        byteArray = blob.getBytes(1, (int) blob.length());
                        imagen = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
                        result.add(new Videojuego(rs.getInt(1), rs.getString(2), rs
                                .getInt(3), rs.getInt(4), rs.getString(5), rs.getString(6),
                                rs.getString(7), rs.getInt(8), imagen));
                    }
                    break;
                case "ordenar":
                    pstmt = connection.prepareStatement("SELECT * FROM VIDEOJUEGO ORDER BY "+params[1]);
                    rs = pstmt.executeQuery();
                    blob = null;
                    byteArray = null;
                    imagen = null;
                    while (rs.next()) {
                        blob = rs.getBlob(9);
                        byteArray = blob.getBytes(1, (int) blob.length());
                        imagen = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
                        result.add(new Videojuego(rs.getInt(1), rs.getString(2), rs
                                .getInt(3), rs.getInt(4), rs.getString(5), rs.getString(6),
                                rs.getString(7), rs.getInt(8), imagen));
                    }
                    break;
            }
            pstmt.close();
            connection.close();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
